﻿using Abp.Specifications;
using Hicap.AuthorizationServer.CommonDto;
using System;
using System.Linq.Expressions;

namespace Hicap.AuthorizationServer.Helper
{
    public static class ExpressionHelper<TEntity>
    {
        public static Expression<Func<TEntity, bool>> CreateExpression(InputFilter filter)
        {
            if (filter == null) return null;
            Expression<Func<TEntity, bool>> andExpression = null;
            foreach (Filter andFilter in filter.AndFilter)
            {
                if (andExpression == null) andExpression = CreateExpression(andFilter);
                else andExpression = andExpression?.And(CreateExpression(andFilter));
            }

            Expression<Func<TEntity, bool>> orExpression = null;
            foreach (Filter orFilter in filter.OrFilter)
            {
                if (orExpression == null) orExpression = CreateExpression(orFilter);
                else orExpression = orExpression?.Or(CreateExpression(orFilter));
            }

            if (andExpression == null && orExpression != null) return orExpression;
            if (andExpression != null && orExpression == null) return andExpression;
            if (andExpression == null && orExpression == null) return null;

            if (filter.Symbol == LinkedSymbol.And) return andExpression.And(orExpression);
            else return andExpression.Or(orExpression);
        }

        public static Expression<Func<TEntity, bool>> CreateExpression(Filter filter)
        {
            if (filter == null) return null;
            dynamic exp = CreateExpression(filter.Field, filter.FkTableName, filter.Value, filter.Symbol);

            if (filter.OtherFilter != null)
            {
                Expression<Func<TEntity, bool>> otherExp = CreateExpression(filter.OtherFilter);
                exp = CreateExpression(filter.LinkedSymbol, exp, otherExp);
            }

            return exp;
        }

        private static Expression<Func<TEntity, bool>> CreateExpression(LinkedSymbol linkedSymbol = LinkedSymbol.And,
            params Expression<Func<TEntity, bool>>[] expression)
        {
            if (expression == null) return null;
            Expression<Func<TEntity, bool>> result = null;

            foreach (Expression<Func<TEntity, bool>> item in expression)
            {
                if (result == null) { result = item; continue; }
                if (item != null) result = linkedSymbol == LinkedSymbol.Or ? result.Or(item) : result.And(item);
            }

            return result;
        }

        public static Expression<Func<TEntity, bool>> CreateExpression(string field, string fKTableName, dynamic value, FilterSymbol symbol = FilterSymbol.Equal)
        {
            ParameterExpression lambdaParam = Expression.Parameter(typeof(TEntity));
            MemberExpression fieldProperty = null;
            if (!string.IsNullOrEmpty(fKTableName))
            {
                MemberExpression fkTable = Expression.PropertyOrField(lambdaParam, fKTableName);
                fieldProperty = Expression.PropertyOrField(fkTable, field);
            }
            else
            {
                fieldProperty = Expression.PropertyOrField(lambdaParam, field);
            }

            value = ConvertHelper.ChangeType(value, fieldProperty.Type);
            Expression lambdaBody;
            switch (symbol)
            {
                case FilterSymbol.Contains:
                    lambdaBody = Expression.Call(
                        fieldProperty,
                        typeof(string).GetMethod("Contains", new Type[] { typeof(string) }),
                        Expression.Constant(value, fieldProperty.Type));
                    break;

                case FilterSymbol.NotContans:
                    lambdaBody = Expression.Not(Expression.Call(
                        fieldProperty,
                        typeof(string).GetMethod("Contains", new Type[] { typeof(string) }),
                        Expression.Constant(value, fieldProperty.Type)));
                    break;

                case FilterSymbol.Greater:
                    lambdaBody = Expression.GreaterThan(fieldProperty, Expression.Constant(value, fieldProperty.Type));
                    break;

                case FilterSymbol.GreaterOrEqual:
                    lambdaBody = Expression.GreaterThanOrEqual(fieldProperty, Expression.Constant(value, fieldProperty.Type));
                    break;

                case FilterSymbol.Less:
                    lambdaBody = Expression.LessThan(fieldProperty, Expression.Constant(value, fieldProperty.Type));
                    break;

                case FilterSymbol.LessOrEqual:
                    lambdaBody = Expression.LessThanOrEqual(fieldProperty, Expression.Constant(value, fieldProperty.Type));
                    break;

                case FilterSymbol.NotEqual:
                    lambdaBody = Expression.NotEqual(fieldProperty, Expression.Constant(value, fieldProperty.Type));
                    break;

                case FilterSymbol.Equal:
                default:
                    lambdaBody = Expression.Equal(fieldProperty, Expression.Constant(value, fieldProperty.Type));
                    break;
            }

            return Expression.Lambda<Func<TEntity, bool>>(lambdaBody, lambdaParam);
        }
    }
}